/*
 * @Author: 吴世扬 18368095041@163.com
 * @Date: 2025-02-13 17:15:29
 * @LastEditors: 吴世扬 18368095041@163.com
 * @LastEditTime: 2025-02-26 08:55:45
 * @FilePath: /vue3-admin-server/src/services/user.service.ts
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, DataSource } from 'typeorm';
import { JwtService } from '@nestjs/jwt';
import { createHash } from 'crypto';

import { UserEntity } from 'src/entities/user.entity';
import { RedisService } from 'src/shareModules/redis/redis.service';
import { LoginByPasswordDTO, UserListDTO, UserDTO } from 'src/common/dto/';
import { arrayToTreeByField } from 'src/common/utils';
import { FileService } from 'src/shareModules/file/file.module';
// import { FindOptionsRelations } from 'typeorm';

@Injectable()
export class UserService {
    constructor(
        @InjectRepository(UserEntity)
        private readonly userRepository: Repository<UserEntity>,
        private readonly jwtService: JwtService,
        private readonly redisService: RedisService,
        private readonly dataSource: DataSource, // 添加: 注入 DataSource
        private readonly fileService: FileService
    ) {}

    async loginByPassword(
        loginDTO: LoginByPasswordDTO
    ): Promise<string | null> {
        const user = await this.userRepository.findOne({
            where: { username: loginDTO.username }
        });
        if (!user) {
            return Promise.resolve(null);
        }

        const isPasswordValid =
            this.passwordEncrypt(loginDTO.password) === user.password;
        if (!isPasswordValid) {
            return Promise.resolve(null);
        }

        const payload: JwtTokenPayload = {
            username: user.username,
            id: user.id
        };
        const token = this.jwtService.sign(payload);
        await this.redisService.setToken(user.id, token);
        return Promise.resolve(token);
    }

    passwordEncrypt(password: string): string {
        return createHash('md5').update(password).digest('hex').toUpperCase();
    }

    logout(userId: number) {
        return this.redisService.deleteToken(userId);
    }

    async readDetail(userId: number): Promise<VO.UserVO | null> {
        const user = await this.userRepository
            .createQueryBuilder('user')
            .leftJoinAndSelect('user.roles', 'role')
            .leftJoinAndSelect('role.menus', 'menu')
            .leftJoinAndSelect('user.depts', 'deptment')
            .leftJoinAndSelect('user.avatar', 'file')
            .where('user.id = :id', { id: userId })
            .getOne();

        if (!user) {
            return null;
        }

        const userVO: VO.UserVO = {
            id: user.id
        };
        userVO.username = user.username;
        userVO.valid = user.valid;
        userVO.roleList = user.roles.map((role) => {
            return {
                menuTree: arrayToTreeByField(
                    role.menus,
                    'id',
                    'parentId',
                    (a, b) => b.sortWeight - a.sortWeight
                ),
                ...role
            };
        });
        if (user.avatar) {
            const { path: _, ...rest } = user.avatar;
            const token = this.fileService.encryptFile(user.avatar);
            userVO.avatar = {
                ...rest,
                token
            };
        }

        userVO.deptList = user.depts;
        userVO.menuTree =
            userVO.roleList.length > 0 ? userVO.roleList[0].menuTree : [];
        userVO.createTime = user.createTime;
        userVO.updateTime = user.updateTime;

        return userVO;
    }

    // async readDetail(userId: number, relations?: FindOptionsRelations<UserEntity>): Promise<UserVO | null> {
    //     const options = relations ? { relations } : {};
    //     return this.userRepository.findOneBy({ id: userId }, options);
    // }

    async readList(
        userListDTO: UserListDTO
    ): Promise<VO.PageDataVO<VO.UserVO>> {
        const { current, size, sortList, username, ...rest } = userListDTO;
        const queryBuilder = this.userRepository
            .createQueryBuilder('user')
            .where(rest);

        // 添加 name 的模糊搜索条件
        if (username) {
            queryBuilder.andWhere('user.username LIKE :username', {
                username: `%${username}%`
            });
        }

        // 应用排序
        if (sortList && sortList.length > 0) {
            sortList.forEach((sort) => {
                queryBuilder.addOrderBy(
                    `user.${sort.column}`,
                    sort.order.toUpperCase() as 'ASC' | 'DESC'
                );
            });
        }

        const total = await queryBuilder.getCount();

        const list = await queryBuilder
            .skip((current - 1) * size)
            .take(size)
            .leftJoinAndSelect('user.roles', 'role')
            .leftJoinAndSelect('role.menus', 'menu')
            .leftJoinAndSelect('user.depts', 'deptment')
            .leftJoinAndSelect('user.avatar', 'avatar')
            .getMany();

        return {
            current,
            size,
            total,
            records: list.map((user) => {
                const { roles, depts, ...rest } = user;
                const roleList = roles.map((role) => {
                    return {
                        menuTree: arrayToTreeByField(
                            role.menus,
                            'id',
                            'parentId',
                            (a, b) => b.sortWeight - a.sortWeight
                        ),
                        ...role
                    };
                });
                const deptList = depts;
                return {
                    ...rest,
                    roleList,
                    deptList
                };
            })
        };
    }

    async save(userDTO: UserDTO): Promise<number | Error> {
        let result: number | Error;
        const queryRunner = this.dataSource.createQueryRunner(); // 修改: 使用 DataSource 创建 QueryRunner

        try {
            await queryRunner.connect();
            await queryRunner.startTransaction();

            const { id, roleIds, deptIds, ...rest } = userDTO;
            if (id) {
                await queryRunner.manager
                    .createQueryBuilder()
                    .update(UserEntity, rest)
                    .where('id = :id', { id })
                    .execute();
                result = id;
            } else {
                const { identifiers } = await queryRunner.manager
                    .createQueryBuilder()
                    .insert()
                    .into(UserEntity)
                    .values(rest)
                    .execute();
                result = identifiers[0].id as number;
            }
            const user = await queryRunner.manager
                .createQueryBuilder(UserEntity, 'user')
                .where({
                    id: result
                })
                .leftJoinAndSelect('user.roles', 'role')
                .leftJoinAndSelect('user.depts', 'department')
                .getOne();
            await queryRunner.manager
                .createQueryBuilder()
                .relation(UserEntity, 'roles')
                .of(id)
                .remove(user?.roles.map((role) => role.id));
            await queryRunner.manager
                .createQueryBuilder()
                .relation(UserEntity, 'depts')
                .of(id)
                .remove(user?.depts.map((dept) => dept.id));

            if (roleIds && roleIds.length > 0) {
                await queryRunner.manager
                    .createQueryBuilder()
                    .relation(UserEntity, 'roles')
                    .of(result)
                    .add(roleIds);
            }
            if (deptIds && deptIds.length > 0) {
                await queryRunner.manager
                    .createQueryBuilder()
                    .relation(UserEntity, 'depts')
                    .of(result)
                    .add(deptIds);
            }

            await queryRunner.commitTransaction();
            return Promise.resolve(result);
        } catch (e) {
            await queryRunner.rollbackTransaction();
            result = e as Error;
            return Promise.reject(result);
        } finally {
            await queryRunner.release();
        }
    }
}
